format ELF64 executable 3
entry start

include 'import64.inc'
interpreter '/lib64/ld-linux-x86-64.so.2'
needed 'libdl.so.2'
import dlopen,dlsym

RTLD_LAZY = 1

macro emit [param] { param }

DEBUG = 0

segment readable writeable executable

start:
    emit < mov rbp,rsp >,< sub rsp,128 >
    emit < fb equ rbp-8 >,< img equ rbp-16 >,< ix equ rbp-20 >,< iy equ rbp-24 >

    mov r13,[dlsym]
if DEBUG = 1
    ; load printf
    emit < xor edi,edi >,< mov rsi,g_str_printf >,< call r13 >,< mov [printf],rax >
end if
    ; load libc functions
    emit < xor edi,edi >,< mov rsi,g_str_malloc >,< call r13 >,< mov [malloc],rax >
    ; load libm
    emit < mov rdi,g_str_libm >,< mov esi,RTLD_LAZY >,< call [dlopen] >,< mov r12,rax >
    ; load libm functions
    emit < mov rdi,r12 >,< mov rsi,g_str_sinf >,< call r13 >,< mov [sinf],rax >
    emit < mov rdi,r12 >,< mov rsi,g_str_cosf >,< call r13 >,< mov [cosf],rax >
    ; alloc memory for the framebuffer (RGBA32F)
    emit < mov edi,IMAGE_WIDTH*IMAGE_HEIGHT*16 >,< call [malloc] >,< mov [fb],rax >

    mov rbx,[fb]
    mov dword [iy],0
      .for_each_y:
    mov dword [ix],0

      .for_each_x:
    ; convert to [-1.0;1.0]
    emit < cvtsi2ss xmm0,[ix] >,< divss xmm0,[g_f32_fb_w] >,<subss xmm0,[g_f32_0p5] >,< addss xmm0,xmm0 >
    ; compute sine and convert to [0.0;1.0]
    emit < mulss xmm0,[g_f32_10] >,< call [sinf] >,< mulss xmm0,[g_f32_0p5] >,< addss xmm0,[g_f32_0p5] >,< movss [rbx+8],xmm0 >

    ; convert to [-1.0;1.0]
    emit < cvtsi2ss xmm0,[iy] >,< divss xmm0,[g_f32_fb_h] >,< subss xmm0,[g_f32_0p5] >,< addss xmm0,xmm0 >
    ; compute sine and convert to [0.0;1.0]
    emit < addss xmm0,[g_f32_0p5] >,< mulss xmm0,[g_f32_10] >,< call [sinf] >,< mulss xmm0,[g_f32_0p5] >,< addss xmm0,[g_f32_0p5] >,< movss [rbx+4],xmm0 >

    add rbx,16

    emit < inc dword [ix] >,< cmp dword [ix],IMAGE_WIDTH >,< jne .for_each_x >
    emit < inc dword [iy] >,< cmp dword [iy],IMAGE_HEIGHT >,< jne .for_each_y >

    ; alloc memory for the image (RGBA8)
    emit < mov edi,IMAGE_WIDTH*IMAGE_HEIGHT*4 >,< call [malloc] >,< mov [img],rax >
    mov ecx,IMAGE_WIDTH*IMAGE_HEIGHT
    pxor xmm1,xmm1
    ; set dst and src pointers
    emit < mov rdi,[img] >,< mov rsi,[fb] >
      @@:
    ; clamp to [0.0; 1.0]
    emit < movaps xmm0,[rsi] >,< maxps xmm0,xmm1 >,< minps xmm0,dqword [g_v4_f32_1] >
    ; convert from [0.0; 1.0] to [0; 255]
    emit < mulps xmm0,dqword [g_v4_f32_255] >,< cvttps2dq xmm0,xmm0 >
    pshufb xmm0,dqword [g_v16_i8_image_conv_mask]
    emit < movd [rdi],xmm0 >,< or dword [rdi],0xff000000 >
    ; continue loop
    emit < add rsi,16 >,< add rdi,4 >,< dec ecx >,< jnz @b >

    ; create TGA file
    emit < mov eax,85 >,< mov rdi,g_tga_name >,< mov esi,110000000b >,syscall,< mov rbx,rax >
    ; write TGA header
    emit < mov eax,1 >,< mov rdi,rbx >,< mov rsi,g_tga_header >,< mov edx,18 >,syscall
    ; write TGA pixel data
    emit < mov eax,1 >,< mov rdi,rbx >,< mov rsi,[img] >,< mov edx,IMAGE_WIDTH*IMAGE_HEIGHT*4 >,syscall
    ; terminate process
    emit < mov eax,60 >,< xor edi,edi >,syscall

align 1
g_str_libm db 'libm.so.6',0
g_str_sinf db 'sinf',0
g_str_cosf db 'cosf',0
g_str_malloc db 'malloc',0
if DEBUG = 1
g_str_printf db 'printf',0
end if

g_tga_name db 'test2.tga',0
g_tga_header db 0,0,2,9 dup 0,(IMAGE_WIDTH and 0x00ff),(IMAGE_WIDTH and 0xff00) shr 8,(IMAGE_HEIGHT and 0x00ff),(IMAGE_HEIGHT and 0xff00) shr 8,32,0

align 4
IMAGE_WIDTH = 800
IMAGE_HEIGHT = 800
g_f32_fb_w dd 800.0
g_f32_fb_h dd 800.0

g_f32_0p5 dd 0.5
g_f32_10 dd 10.0

align 8
sinf dq 0
cosf dq 0
malloc dq 0
if DEBUG = 1
printf dq 0
end if

align 16
g_v4_f32_1 dd 1.0,1.0,1.0,1.0
g_v4_f32_255 dd 255.0,255.0,255.0,255.0
g_v16_i8_image_conv_mask db 0,4,8,12,12 dup 0x80
